package com.ubs.lingua.buildtool.cs;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Future;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import com.ubs.lingua.buildtool.cs.parameters.BuildParameters;
import com.ubs.lingua.buildtool.cs.parameters.CscParameters;
import com.ubs.lingua.buildtool.cs.utils.RepositoryUtils;
import com.ubs.lingua.buildtool.cs.utils.ant.CSharpCompiler;
import com.ubs.lingua.buildtool.cs.utils.ant.Expander;
import com.ubs.lingua.buildtool.cs.utils.ant.Packer;

/**
 * The C# library builder singleton Spring bean.
 */
@Component
public class CsBuilderBean implements CsBuilder {

    private static final Log LOG = LogFactory.getLog(CsBuilderBean.class);

    @Async
    public Future<Resource> build(final BuildParameters parameters, final String packagePath)
            throws InterruptedException, IOException {
        LOG.info("Staring building batch. Assembly name: " + parameters.getAssemblyName() + " Package path: "
                + packagePath);
        final Resource packageRes = new FileSystemResource(packagePath);
        final Resource unpackDirRes = packageRes.createRelative(RepositoryUtils.UNPACK_DIR);

        LOG.info("Unpacking package to " + unpackDirRes.getFile().getPath());
        Expander.antUnzip(packageRes.getFile(), unpackDirRes.getFile());

        LOG.info("Scanning for the package root");
        final File root = RepositoryUtils.findPackageRoot(unpackDirRes.getFile());
        if (null == root) {
            final CsBuilderException exc = new CsBuilderException("Unable to find package's root directory");
            LOG.error(exc.getMessage(), exc);
            throw exc;
        }
        LOG.info("Package root is " + root.getPath());
        final File destination = RepositoryUtils.ensureUnique(new File(root, RepositoryUtils.ASSEMBLY_DIR));

        LOG.info("Building assembly from " + root.getPath());
        // checking whether it is necessary to build assembly for several platforms
        final Set<CscParameters.Platform> targetPlatforms = new HashSet<CscParameters.Platform>();
        targetPlatforms.add(parameters.getCscParams().getAssemblyPlatform());
        if (parameters.getCscParams().isMultyPlatform()) {
            targetPlatforms.addAll(Arrays.asList(CscParameters.Platform.values()));
            LOG.info("Multi-platform build requested for the " + parameters.getAssemblyName() + " assembly");
        }

        // checking whether it is necessary to build assembly for several configurations
        final Set<CscParameters.Configuration> targetConfigs = new HashSet<CscParameters.Configuration>();
        targetConfigs.add(parameters.getCscParams().getAssemblyConfiguration());
        if (parameters.getCscParams().isMultyConfig()) {
            targetConfigs.addAll(Arrays.asList(CscParameters.Configuration.values()));
            LOG.info("Multi-configuration build requested for the " + parameters.getAssemblyName() + " assembly");
        }

        for (CscParameters.Platform platform : targetPlatforms) {
            for (CscParameters.Configuration configuration : targetConfigs) {
                LOG.info("Building assembly " + parameters.getAssemblyName() + " " + platform.toString() + " "
                        + configuration.toString());
                final File destDir = RepositoryUtils.ensureUnique(new File(destination, platform.toString() + "/"
                        + configuration.toString()));
                CSharpCompiler.antCsc(new File(root, RepositoryUtils.SRC_DIR), //
                                      new File(root, RepositoryUtils.DEPS_LIBS_DIR), destDir, //
                                      parameters.getAssemblyName(), configuration, //
                                      parameters.getCscParams().isGenerateXmlDoc(), //
                                      parameters.getCscParams().isUnsafe(), platform);
            }
        }

        LOG.info("Packing assemblies");
        final Resource packedAsmblRes = new FileSystemResource(new File(root, RepositoryUtils.ASSEMBLY_PKG));
        Packer.antZip(destination, packedAsmblRes.getFile(), parameters.getPackerSettings());
        LOG.info("Finished building assembly " + parameters.getAssemblyName() + " Batch path: " + packagePath);

        return new AsyncResult<Resource>(packedAsmblRes);
    }
}
